package real.lang.reflect;

import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;

import real.lang.*;
import real.lang.literal.RealBoolean;
import real.lang.set.*;

public final class RealRTI {

	public static final String DEFAULT_TYPE_SPACE = "default";

	private static RealRTI instance = null;

	private RealRTI() throws RealLangException {
		this.ConstructTypes();
		this.ConstructTypeRelations();
	};

	public static synchronized RealRTI getInstance() throws RealLangException {
		if (instance == null)
			instance = new RealRTI();
		return instance;
	}

	/**
	 * TypeSpace -> ( enumeration type name, real type)
	 */
	private HashMap<String, HashMap<String, RealType>> types = new HashMap<String, HashMap<String, RealType>>();

	private RealType getType(String space, String name)
			throws RealLangException {
		if (!this.types.containsKey(space))
			throw new RealLangException(
					"RTI Error: type space does not exists " + space + ".");
		if (!this.types.get(space).containsKey(name))
			throw new RealLangException(
					"RTI Error: type space does not contain type " + name);
		return this.types.get(space).get(name);
	}

	private RealType getType(RealTypeSignature signature)
			throws RealLangException {
		return getType(signature.getTypeSpace(), signature.toString());
	}

	private void setType(String space, String name, RealType value) {
		if (!this.types.containsKey(space))
			this.types.put(space, new HashMap<String, RealType>());
		this.types.get(space).put(name, value);
	}

	public Collection<RealType> getTypes() {
		return this.types.get(DEFAULT_TYPE_SPACE).values();
	}

	public RealType GetType(EnumerationOfRealTypes realType,
			Collection<EnumerationOfRealSubtypes> realSubtypes)
			throws RealLangException {
		return GetType(new RealTypeSignature(realType, realSubtypes,
				DEFAULT_TYPE_SPACE));
	}

	public RealType GetType(String typespace, EnumerationOfRealTypes realType,
			Collection<EnumerationOfRealSubtypes> realSubtypes)
			throws RealLangException {
		return GetType(new RealTypeSignature(realType, realSubtypes, typespace));
	}

	public RealType GetType(RealTypeSignature signature)
			throws RealLangException {
		return this.getType(signature);
	}

	/**
	 * typespace -> typerelation
	 */
	private HashMap<String, RealTypeRelation> typeRelations = new HashMap<String, RealTypeRelation>();

	public RealTypeRelation GetSubtypeRelation(String typeSpace) {
		return this.typeRelations.get(typeSpace);
	}

	private void constructType(RealTypeSignature signature,
			Class<? extends RealClass> realClass) {
		RealType value = RealTypeFactory.CreateType(signature, realClass);
		this.setType(signature.getTypeSpace(), value.getName(), value);
	}

	/**
	 * Construct default real typing
	 */
	private void ConstructTypes() {
		// RealSet
		this.constructType(new RealTypeSignature(EnumerationOfRealTypes.SET,
				EnumSet.noneOf(EnumerationOfRealSubtypes.class),
				DEFAULT_TYPE_SPACE), RealSet.class);

		// FiniteSet
		this.constructType(FiniteSet.getRealTypeSignature(DEFAULT_TYPE_SPACE),
				FiniteSet.class);

		// NaturalNumbers
		this
				.constructType(NaturalNumbers
						.getRealTypeSignature(DEFAULT_TYPE_SPACE),
						NaturalNumbers.class);

		// Literals
		this.constructType(
				RealBoolean.getRealTypeSignature(DEFAULT_TYPE_SPACE),
				RealBoolean.class);

	}

	/**
	 * Construct default relations for real typing
	 * 
	 * @throws RealLangException
	 */
	private void ConstructTypeRelations() throws RealLangException {
		// default type space
		RealTypeRelation isSubtype = new RealTypeRelation("isSubtype");

		RealType set = this.GetType(EnumerationOfRealTypes.SET, EnumSet
				.noneOf(EnumerationOfRealSubtypes.class));
		RealType numbersSet = this.GetType(EnumerationOfRealTypes.SET, EnumSet
				.of(EnumerationOfRealSubtypes.NUMBERS));
		isSubtype.set(numbersSet, set);

		this.typeRelations.put(DEFAULT_TYPE_SPACE, isSubtype);
	}

}
